import os
import shutil
import sys
import threading
import time
from typing import Final

import flet as ft
from tkinter import filedialog, messagebox

from flet.core import colors

from SmartBackup import SmartBackup

CHOOSE_SOURCE_STR: Final[str] = "请选择需要备份的文件目录"
CHOOSE_TARGET_STR: Final[str] = "请选择导出的文件目录"
CONFIG_FILE: Final[str] = "config.txt"
CONFIG_FILE_LOCAL: Final[str] = "config/"
CONFIG_FILE_LOCAL_PATH: Final[str] = CONFIG_FILE_LOCAL + CONFIG_FILE
CONFIG_FILE_USER: Final[str] = "C:\\Users\\admin\\AppData\\Local\\PythonUDiskSync\\config\\"
CONFIG_FILE_USER_PATH: Final[str] = CONFIG_FILE_USER + CONFIG_FILE


class FletMainWindow:
    def __init__(self, page):
        self.page = page
        self.sourceFilePath = None
        self.targetFilePath = None
        self.createView()
        self.createFileChooseView(page)
        self.createInfoList(page)
        # 加载本地用户数据，获取上一次备份过的目录
        data = self.loadConfig()
        if data.get('sourceFilePath'):
            self.sourceFilePath = data['sourceFilePath']
            self.sourceFile.value = self.sourceFilePath
        if data.get('targetFilePath'):
            self.targetFilePath = data['targetFilePath']
            self.targetFile.value = self.targetFilePath
        self.page.update()

    # 创建主窗口
    def createView(self):
        self.page.title = "U盘备份助手"
        # 设置窗口尺寸
        self.page.window.width = 600  # 窗口宽度为800像素
        self.page.window.height = 550  # 窗口高度为600像素
        # 禁止用户调整窗口大小
        self.page.window.resizable = False
        self.page.window.center()
        # root.iconphoto(True, tk.PhotoImage(file=self.resource_path('./img/logo.png')))

    # 选择源文件夹
    def chooseSourceFile(self, e):
        self.sourceFilePath = self.openFileWindow(self.sourceFilePath)
        self.sourceFile.value = self.sourceFilePath
        self.page.update()

    # 选择目标文件夹
    def chooseTargetFile(self, e):
        self.targetFilePath = self.openFileWindow(self.targetFilePath)
        self.targetFile.value = self.targetFilePath
        self.page.update()

    # 打开文件夹选择窗口
    def openFileWindow(self, filePath):
        if filePath is None:
            filePath = 'C:/'
        return filedialog.askdirectory(
            title='请选择文件夹',  # 设置对话框标题:cite[7]:cite[9]:cite[10]
            initialdir=filePath  # 设置初始目录，例如C盘根目录:cite[7]:cite[10]
        )

    # 显示错误信息提示弹窗
    def showToastWindow(self, infoStr):
        messagebox.showinfo(infoStr, infoStr)

    def createFileChooseView(self, page):
        self.sourceFile = ft.TextField(hint_text=CHOOSE_SOURCE_STR, width=300)
        self.sourceFile.disabled = True
        page.add(ft.Row([self.sourceFile, ft.ElevatedButton("浏览目录", on_click=self.chooseSourceFile)]))
        self.targetFile = ft.TextField(hint_text=CHOOSE_TARGET_STR, width=300)
        self.targetFile.disabled = True
        page.add(ft.Row([self.targetFile, ft.ElevatedButton("浏览目录", on_click=self.chooseTargetFile)]))
        self.deleteDiffBtn = ft.Checkbox(label='是否删除源文件夹不存在的文件（谨慎勾选！！！）',
                                         on_change=self.deleteDiffBtnChange)
        page.add(self.deleteDiffBtn)
        page.add(ft.Row([ft.ElevatedButton("开 始 备 份", on_click=self.startCopy),
                         ft.ElevatedButton("清空控制台信息", on_click=self.clearLog)]))
        # 创建AlertDialog实例
        self.confirm_dialog = ft.AlertDialog(
            modal=True,  # 是否为模态对话框（阻止与其他页面元素交互）
            title=ft.Text("请注意确认！"),  # 对话框标题
            content=ft.Text("勾选删除操作会删除导出文件夹中存在，而源备份文件夹中不存在的文件，您确定要继续吗？"),  # 对话框内容
            actions=[  # 对话框操作按钮
                ft.TextButton("确认", on_click=lambda e: self.yes_clicked(e)),  # 确认按钮
                ft.TextButton("取消", on_click=lambda e: self.no_clicked(e)),  # 取消按钮
            ],
            actions_alignment=ft.MainAxisAlignment.END,  # 按钮对齐方式
        )
        self.page.overlay.append(self.confirm_dialog)

    def deleteDiffBtnChange(self, e):
        if e.control.value:
            self.confirm_dialog.modal = True
            self.confirm_dialog.open = True  # 确保设置 open 属性为 True
            self.page.update()

    def yes_clicked(self, e):
        """点击'是'按钮的处理函数"""
        self.confirm_dialog.modal = False
        self.confirm_dialog.open = False
        self.page.update()

    def no_clicked(self, e):
        """点击'否'按钮的处理函数"""
        self.confirm_dialog.modal = False
        self.confirm_dialog.open = False
        self.deleteDiffBtn.value = False
        self.page.update()

    # 尝试开始备份
    def startCopy(self, e):
        if self.sourceFilePath is None:
            self.showToastWindow(CHOOSE_SOURCE_STR + '!')
            return
        if self.targetFilePath is None:
            self.showToastWindow(CHOOSE_TARGET_STR + '!')
            return
        if self.targetFilePath == self.sourceFilePath:
            self.showToastWindow('两个目录不能相同!')
            return
        self.saveConfig()
        threading.Thread(target=self.run, name='备份线程').start()

    # 拷贝过程放到子线程进行
    def run(self):
        self.backupFolder(self.sourceFilePath, self.targetFilePath)

    # 备份文件夹
    def backupFolder(self, src, dest):
        backup_system = SmartBackup(self,
                                    source_dir=src,
                                    backup_dir=dest
                                    )
        backup_system.backup(deleteDiff=self.deleteDiffBtn.value)
        # 重置删除按钮选中状态，避免用户误操作
        self.deleteDiffBtn.value = False

    def consoleCallback(self, message, message_type="I"):

        text_color = self.color_map.get(message_type, colors.Colors.BLACK)
        # 创建一个带时间戳的文本控件
        timestamp = time.strftime("%H:%M:%S")
        log_text = ft.Text(value=f"[{timestamp}] {message}", color=text_color, selectable=True)  # 允许文本选择
        # 将文本控件添加到ListView中
        self.console_output.controls.append(log_text)
        # 如果设置了自动滚动，则滚动到底部
        if self.console_output.auto_scroll:
            self.console_output.scroll_to(offset=float("inf"), duration=5000)  # 滚动到无限远（底部）
        # 更新页面以刷新显示
        self.page.update()

    # 创建日志输出控件
    def createInfoList(self, page):
        self.color_map = {
            "D": colors.Colors.BLUE,
            "I": colors.Colors.GREEN,
            "W": colors.Colors.ORANGE,
            "E": colors.Colors.RED
        }
        # 可以根据消息类型设置不同的文本颜色
        self.console_output = ft.ListView(
            expand=True,  # 关键：设置为可扩展以占据可用空间
            spacing=10,  # 设置行间距
            padding=20,  # 设置内边距
            auto_scroll=True,  # 可选：是否自动滚动到底部
        )
        # 注意：也需要为Column提供高度约束，例如放在一个固定高度的Container里
        page.add(
            ft.Text("日志输出:"),
            ft.Container(  # 用一个Container包裹ListView并设置高度/边框
                self.console_output,
                border_radius=ft.border_radius.all(5),
                border=ft.border.all(2, colors.Colors.BLACK),
                expand=True,  # Container也 expand 以填充空间
                height=400  # 或者设置一个固定高度
            )
        )

    def clearLog(self, e):
        self.console_output.clean()
        self.page.update()

    # 保存用户数据
    def saveConfig(self):
        self.consoleCallback('配置保存路径：\n' + CONFIG_FILE_USER_PATH)
        with open(CONFIG_FILE_USER_PATH, "w", encoding='utf-8') as f:
            f.write('sourceFilePath*=' + self.sourceFilePath + '\n')
            f.write('targetFilePath*=' + self.targetFilePath + '\n')
            f.close()

    # 加载本地用户数据
    def loadConfig(self):
        if not os.path.exists(CONFIG_FILE_USER_PATH):
            try:
                os.makedirs(CONFIG_FILE_USER, exist_ok=True)
                shutil.copy2(str(self.resource_path(CONFIG_FILE_LOCAL_PATH)), CONFIG_FILE_USER_PATH)
            except Exception as e:
                print(f"配置文件复制失败: {e}")
            return {}
        data = []
        dataMap = {}
        with open(CONFIG_FILE_USER_PATH, "r", encoding='utf-8') as f:
            data = f.readlines()
            f.close()
        if len(data) == 0:
            return {}
        for d in data:
            key, value = d.split("*=")
            dataMap[key] = value.replace("\n", "")
        return dataMap

    def resource_path(self, relative_path):
        """ 获取资源的绝对路径。
        参数:
            relative_path (str): 资源的相对路径
        返回:
            str: 资源的绝对路径
        """
        try:
            # 打包后的临时文件夹
            base_path = sys._MEIPASS
        except AttributeError:
            # 正常运行的当前目录
            base_path = os.path.abspath(".")
        return os.path.join(base_path, relative_path)
